/*!
 * \file xml_header.c
 *
 * \brief 消息头操作
 *
 * All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.
 *
 * \version 1.0
 * \author songbb
 * \date 2016/08/08
 *
 */

#include <stdio.h>
#include <endian.h>
#include <frct_comm.h>
#include <frxml_parse.h>
#include <ctype.h>
#include <frct_detect_hash.h>

#include "xml_comm.h"
#include "xml_header.h"
#include "config.h"

/*!
 * 获取包头长度,假设包头的最大长度为12个字节,"<?xml"前的部分为包头
 *
 * \param p_payload             (数据内容)
 * \param ui_payload_len        (数据长度)
 *
 * \return 0  失败\n
 *         1  成功
 *
 */
FR_STATIC int __xml_header_get_hdr_flag_len(char *p_payload
                                            , unsigned int ui_payload_len)
{
    int i_loop;

    for (i_loop = 0; i_loop < (int) ui_payload_len && i_loop <= APM_XML_PKT_HDR_LEN_MAX; i_loop++)
    {
        if (*(p_payload + i_loop) == '<' &&
            *(p_payload + i_loop + 1) == '?' &&
            *(p_payload + i_loop + 2) == 'x' &&
            *(p_payload + i_loop + 3) == 'm' &&
            *(p_payload + i_loop + 4) == 'l')
            return i_loop;
    }

    return -1;
}


/*!
 * 将包长与包头中的字段进行匹配测试
 *
 * \param p_st_detect_sess          (Detect的Session)
 * \param i_hdrlen_max              (包长字段的最大长度)
 *
 * \return 0  失败\n
 *         1  成功
 *
 */
FR_STATIC int __xml_cmp_hdrlen(xml_detect_session *p_st_detect_sess
                            , int i_hdrlen_max
                            , char *p_len)
{
    XML_PRINT_INFO("__xml_cmp_hdrlen...\n");
    long long *p_ll_hdrlen;
    long long ll_hdrlen;

    ll_hdrlen = atol(p_len);
    if (ll_hdrlen == p_st_detect_sess->ui_pkt_len)
    {
        XML_PRINT_DBG("p_len:[%s] ll_hdrlen:[%lld] p_st_detect_sess->ui_pkt_len:[%u]\n", p_len, ll_hdrlen, p_st_detect_sess->ui_pkt_len);
        if (i_hdrlen_max == 8)
        {
            // 由8个字节的ASCII码组成,表示包体长度(包含包头和XML)
            p_st_detect_sess->hdr_type = APM_XML_HDR_TYPE_8ASCII_TOTAL;
            return FR_SUCC;
        }
        else if (i_hdrlen_max == 4)
        {
            // 由4个字节的ASCII码组成,表示包体长度(包含包头和XML)
            p_st_detect_sess->hdr_type = APM_XML_HDR_TYPE_4ASCII_TOTAL;
            return FR_SUCC;
        }
        return FR_FAIL;
    }
    else if (ll_hdrlen == p_st_detect_sess->ui_pkt_len - p_st_detect_sess->ui_hdr_flag_len)
    {
        XML_PRINT_DBG("p_len:[%s] ll_hdrlen:[%lld] p_st_detect_sess->ui_pkt_len:[%u]\n", p_len, ll_hdrlen, p_st_detect_sess->ui_pkt_len);
        if (i_hdrlen_max == 8)
        {
            // 由8个字节的ASCII码组成,表示XML长度
            p_st_detect_sess->hdr_type = APM_XML_HDR_TYPE_8ASCII_XML;
            return FR_SUCC;
        }
        else if (i_hdrlen_max == 4)
        {
            // 由4个字节的ASCII码组成,表示XML长度
            p_st_detect_sess->hdr_type = APM_XML_HDR_TYPE_4ASCII_XML;
            return FR_SUCC;
        }
        return FR_FAIL;
    }
    else
    {
        p_ll_hdrlen = (long long *) p_len;
        if (i_hdrlen_max == 8)
            ll_hdrlen = (long long) be64toh(*p_ll_hdrlen);
        else if (i_hdrlen_max == 4)
            ll_hdrlen = (long) ntohl(*p_ll_hdrlen);
        XML_PRINT_DBG("p_len:[%s] ll_hdrlen:[%lld] p_st_detect_sess->ui_pkt_len:[%u]\n", p_len, ll_hdrlen, p_st_detect_sess->ui_pkt_len);

        if (ll_hdrlen == p_st_detect_sess->ui_pkt_len)
        {
            if (i_hdrlen_max == 8)
            {
                // 由8个字节的二进制组成,表示包体长度(包含包头和XML)
                p_st_detect_sess->hdr_type = APM_XML_HDR_TYPE_8BIN_TOTAL;
                return FR_SUCC;
            }
            else if (i_hdrlen_max == 4)
            {
                // 由4个字节的二进制组成,表示包体长度(包含包头和XML)
                p_st_detect_sess->hdr_type = APM_XML_HDR_TYPE_4BIN_TOTAL;
                return FR_SUCC;
            }
            return FR_FAIL;
        }
        else if (ll_hdrlen == p_st_detect_sess->ui_pkt_len - p_st_detect_sess->ui_hdr_flag_len)
        {
            if (i_hdrlen_max == 8)
            {
                // 由8个字节的二进制组成,表示XML长度
                p_st_detect_sess->hdr_type = APM_XML_HDR_TYPE_8BIN_XML;
                return FR_SUCC;
            }
            else if (i_hdrlen_max == 4)
            {
                // 由4个字节的二进制组成,表示XML长度
                p_st_detect_sess->hdr_type = APM_XML_HDR_TYPE_4BIN_XML;
                return FR_SUCC;
            }
            return FR_FAIL;
        }
        else
            return FR_FAIL;
    }

    return FR_FAIL;
}

/*!
 * 分析包头中的表示长度的字段
 *
 * \param p_st_detect_sess          (Detect的Session)
 * \param i_hdrlen_max              (包长字段的最大长度)
 *
 * \return 0  失败\n
 *         1  成功
 *
 */
FR_STATIC int __xml_analyze_hdrlen(xml_detect_session *p_st_detect_sess
                                , int i_hdrlen_max)
{
    XML_PRINT_INFO("__xml_analyze_hdrlen...\n");
    char arr_len[9] = {'\0'};
    int i_loop;

    PRINT_HEX_STRING("a", p_st_detect_sess->arr_hdr, sizeof(p_st_detect_sess->arr_hdr));

    // 取最前面的几个字节
    for (i_loop = 0; i_loop < i_hdrlen_max; i_loop++)
    {
        arr_len[i_loop] = p_st_detect_sess->arr_hdr[i_loop];
        if (i_loop == i_hdrlen_max - 1)
        {
            if (__xml_cmp_hdrlen(p_st_detect_sess, i_hdrlen_max, arr_len) == FR_SUCC)
            {
                p_st_detect_sess->i_hdrlen_from_head = FR_YES;
                return FR_SUCC;
            }
        }
    }

    // 取最后面的几个字节
    for (i_loop = p_st_detect_sess->ui_hdr_flag_len - i_hdrlen_max; (unsigned int) i_loop < p_st_detect_sess->ui_hdr_flag_len; i_loop++)
    {
        arr_len[i_loop - (p_st_detect_sess->ui_hdr_flag_len - i_hdrlen_max)] = p_st_detect_sess->arr_hdr[i_loop];
        if ((unsigned int) i_loop == p_st_detect_sess->ui_hdr_flag_len - 1)
        {
            if (__xml_cmp_hdrlen(p_st_detect_sess, i_hdrlen_max, arr_len) == FR_SUCC)
            {
                p_st_detect_sess->i_hdrlen_from_head = FR_NO;
                return FR_SUCC;
            }
        }
    }

    return FR_FAIL;
}

/*!
 * 分析包头类型
 *
 * \param p_st_detect_sess         (Detect的Session)
 *
 * \return 0  失败\n
 *         1  成功
 *
 */
FR_STATIC int __xml_analyze_hdr_type(xml_detect_session *p_st_detect_sess)
{
    XML_PRINT_INFO("__xml_analyze_hdr_type...\n");

    if (p_st_detect_sess->arr_hdr == '\0' && p_st_detect_sess->ui_hdr_flag_len == 0)
    {
        p_st_detect_sess->hdr_type = APM_XML_HDR_TYPE_ZERO;
        return FR_SUCC;
    }

    if (p_st_detect_sess->ui_hdr_flag_len >= 8)
    {
        if (__xml_analyze_hdrlen(p_st_detect_sess, 8) == FR_SUCC)
        {
            XML_PRINT_CRIT("hdr_type:[%s] hdrlen_from_head:[%s]\n", XML_HDR_TYPE2STR(p_st_detect_sess->hdr_type),
                           p_st_detect_sess->i_hdrlen_from_head == FR_YES ? "YES" : "NO");
            return FR_SUCC;
        }

        if (__xml_analyze_hdrlen(p_st_detect_sess, 4) == FR_SUCC)
        {
            XML_PRINT_CRIT("hdr_type:[%s] hdrlen_from_head:[%s]\n", XML_HDR_TYPE2STR(p_st_detect_sess->hdr_type),
                           p_st_detect_sess->i_hdrlen_from_head == FR_YES ? "YES" : "NO");
            return FR_SUCC;
        }
    }
    else if (p_st_detect_sess->ui_hdr_flag_len >= 4)
    {
        if (__xml_analyze_hdrlen(p_st_detect_sess, 4) == FR_SUCC)
        {
            XML_PRINT_CRIT("hdr_type:[%s] hdrlen_from_head:[%s]\n", XML_HDR_TYPE2STR(p_st_detect_sess->hdr_type),
                           p_st_detect_sess->i_hdrlen_from_head == FR_YES ? "YES" : "NO");
            return FR_SUCC;
        }
    }

    return FR_FAIL;
}

/*!
 * 分析包头
 *
 * \param p_st_detect_sess          (Detect的Session)
 * \param p_analyze_in              (协议分析入力数据)
 *
 * \return 0  失败\n
 *         1  成功
 *
 */
FR_STATIC int __xml_header_analyze(xml_detect_session *p_st_detect_sess
                                    , Apm_packet *p_analyze_in)
{
    int i_hdr_flag_len;

    p_st_detect_sess->ui_dst_ip = p_analyze_in->dst_ip;
    p_st_detect_sess->us_dst_port = p_analyze_in->dst_port;
    p_st_detect_sess->ui_pkt_len = p_analyze_in->ui_payload_len;

    i_hdr_flag_len = __xml_header_get_hdr_flag_len(p_analyze_in->p_payload, p_analyze_in->ui_payload_len);
    XML_PRINT_WARN("flagLen:[%d]\n", i_hdr_flag_len);

    if (i_hdr_flag_len < 0 || (int) p_analyze_in->ui_payload_len <= i_hdr_flag_len)
        goto error;

    // 判断是否是XML传输格式
    if (frxml_isxml(p_analyze_in->p_payload + i_hdr_flag_len, p_analyze_in->ui_payload_len - i_hdr_flag_len) != FR_SUCC)
        goto error;

    if (i_hdr_flag_len == 0)
    {
        p_st_detect_sess->hdr_type = APM_XML_HDR_TYPE_ZERO;
        XML_PRINT_CRIT("hdr_type:[%s]\n", XML_HDR_TYPE2STR(p_st_detect_sess->hdr_type));
    }
    else
    {
        p_st_detect_sess->hdr_type = APM_XML_HDR_TYPE_UNKNOWN;
        if (i_hdr_flag_len > (int) sizeof(p_st_detect_sess->arr_hdr))
        {
            memcpy(p_st_detect_sess->arr_hdr, p_analyze_in->p_payload, sizeof(p_st_detect_sess->arr_hdr));
            p_st_detect_sess->ui_hdr_flag_len = (unsigned int) sizeof(p_st_detect_sess->arr_hdr);
        }
        else
        {
            memcpy(p_st_detect_sess->arr_hdr, p_analyze_in->p_payload, i_hdr_flag_len);
            p_st_detect_sess->ui_hdr_flag_len = (unsigned int) i_hdr_flag_len;
        }
    }

    return FR_SUCC;
error:
    return FR_FAIL;
}

int xml_header_detect(Conntrack *p_st_conn
                        , Apm_packet *p_analyze_in)
{
    XML_PRINT_INFO("xml_header_detect...\n");
    xml_detect_session *p_st_detect_sess = NULL;

    if ((p_st_detect_sess = (xml_detect_session *) frct_detect_data_get(p_st_conn, MODULE_L7PROTO)) == NULL)
    {
        if ((p_st_detect_sess = (xml_detect_session *) frmalloc(sizeof(xml_detect_session))) == NULL)
        {
            XML_MALLOCK_ERR;
            goto error;
        }
        memset(p_st_detect_sess, 0x00, sizeof(xml_detect_session));
        p_st_detect_sess->hdr_type = APM_XML_HDR_TYPE_INIT;

        if (frct_detect_hash_add(p_st_conn, MODULE_L7PROTO, (void *) p_st_detect_sess) != FR_SUCC)
        {
            xml_free_detect_session(&p_st_detect_sess);
            XML_PRINT_EMERG("add detect session failed\n");
            goto error;
        }

        XML_PRINT_NOTICE("add detect session success\n");
    }

    if (p_st_detect_sess->hdr_type == APM_XML_HDR_TYPE_INIT)
    {
        if (__xml_header_analyze(p_st_detect_sess, p_analyze_in) != FR_SUCC)
            goto error;
        if (p_st_detect_sess->hdr_type == APM_XML_HDR_TYPE_ZERO)
            return FR_SUCC;
    }
    else
    {
        if (p_st_detect_sess->ui_dst_ip == p_analyze_in->src_ip && p_st_detect_sess->us_dst_port == p_analyze_in->src_port)
        {
            // 出现相反方向的数据包,开始用前面的数据包判断协议类型
            if (p_st_detect_sess->hdr_type == APM_XML_HDR_TYPE_ZERO)
                return FR_SUCC;
            else
            {
                if (__xml_analyze_hdr_type(p_st_detect_sess) == FR_SUCC)
                    return FR_SUCC;

                // 协议头匹配失败,有可能是丢包引起。开始初始化以匹配另一个方向的数据包
                if (__xml_header_analyze(p_st_detect_sess, p_analyze_in) != FR_SUCC)
                    goto error;
            }
        }
        else if (p_st_detect_sess->ui_dst_ip == p_analyze_in->dst_ip && p_st_detect_sess->us_dst_port == p_analyze_in->dst_port)
        {
            // 继续等待相反方向的包
            p_st_detect_sess->ui_pkt_len += p_analyze_in->ui_payload_len;
        }
        else
            goto error;
    }

    return FR_FAIL;
error:
    xml_init_detect_session(&p_st_detect_sess);
    return FR_FAIL;
}

int xml_header_unpack(char *p_payload
                       , unsigned int ui_payload_len
                       , L7session_xml *p_st_sess
                       , xml_header *p_st_xml_header)
{
    char arr_len[9] = {'\0'};
    long long *p_ll_hdrlen;
    long long ll_hdrlen;

    if (ui_payload_len < p_st_sess->ui_hdr_flag_len)
        goto error;

    if (p_st_sess->hdr_type == APM_XML_HDR_TYPE_UNKNOWN)
        goto error;
    else
    {
        switch (p_st_sess->hdr_type)
        {
            case APM_XML_HDR_TYPE_4BIN_TOTAL:
            case APM_XML_HDR_TYPE_4BIN_XML:
                if (p_st_sess->i_hdrlen_from_head == FR_NO)
                    p_ll_hdrlen = (long long *) (p_payload + (p_st_sess->ui_hdr_flag_len - 4));
                else
                    p_ll_hdrlen = (long long *) p_payload;
                ll_hdrlen = (long) ntohl(*p_ll_hdrlen);
                break;
            case APM_XML_HDR_TYPE_4ASCII_TOTAL:
            case APM_XML_HDR_TYPE_4ASCII_XML:
                if (p_st_sess->i_hdrlen_from_head == FR_NO)
                    strncpy(arr_len, p_payload + (p_st_sess->ui_hdr_flag_len - 4), 4);
                else
                    strncpy(arr_len, p_payload, 4);
                ll_hdrlen = atol(arr_len);
                break;
            case APM_XML_HDR_TYPE_8BIN_TOTAL:
            case APM_XML_HDR_TYPE_8BIN_XML:
                if (p_st_sess->i_hdrlen_from_head == FR_NO)
                    p_ll_hdrlen = (long long *) (p_payload + (p_st_sess->ui_hdr_flag_len - 8));
                else
                    p_ll_hdrlen = (long long *) p_payload;
                ll_hdrlen = (long long) be64toh(*p_ll_hdrlen);
                break;
            case APM_XML_HDR_TYPE_8ASCII_TOTAL:
            case APM_XML_HDR_TYPE_8ASCII_XML:
                if (p_st_sess->i_hdrlen_from_head == FR_NO)
                    strncpy(arr_len, p_payload + (p_st_sess->ui_hdr_flag_len - 8), 8);
                else
                    strncpy(arr_len, p_payload, 8);
                ll_hdrlen = atol(arr_len);
                break;
            default:
                ;
        }
        switch (p_st_sess->hdr_type)
        {
            case APM_XML_HDR_TYPE_4BIN_TOTAL:
            case APM_XML_HDR_TYPE_4ASCII_TOTAL:
            case APM_XML_HDR_TYPE_8BIN_TOTAL:
            case APM_XML_HDR_TYPE_8ASCII_TOTAL:
                p_st_xml_header->ui_pkt_len = (unsigned int) ll_hdrlen;
                break;
            case APM_XML_HDR_TYPE_4BIN_XML:
            case APM_XML_HDR_TYPE_4ASCII_XML:
            case APM_XML_HDR_TYPE_8BIN_XML:
            case APM_XML_HDR_TYPE_8ASCII_XML:
                p_st_xml_header->ui_pkt_len = (unsigned int) (ll_hdrlen + p_st_sess->ui_hdr_flag_len);
                break;
            default:
                ;
        }
    }

    p_st_xml_header->ui_header_len = p_st_sess->ui_hdr_flag_len;

    return FR_SUCC;
error:
    return FR_FAIL;
}
